<?php
if (!defined('APPLICATION_PATH'))
	die('APPLICATION_PATH not defined!');

defined('ADMIN_PATH') || define('ADMIN_PATH', realpath(dirname(__FILE__)) . '/../');
defined('FILES_PATH') || define('FILES_PATH', ADMIN_PATH . 'configs/');

class Admin_InstallController extends Zend_Controller_Action {

	private $db_table;
	private $tables;
	
	private $model_prefix = '';
	private $model_sufix = 'Model';
	private $model_file;
	private $model_path;
	private $model_izi_path;
	private $model_abstract_file;
	private $model_abstract_name;
	private $model_getset_file;
	private $model_getfk_file;
	
	private $dbtable_prefix = '';
	private $dbtable_sufix = 'DbTable';
	private $dbtable_file;
	private $dbtable_path;
	private $dbtable_izi_path;
	private $dbtable_abstract_file;
	private $dbtable_abstract_name;
	private $dbtable_referencemap_file;
	private $dbtable_find_file;
	
	private $form_prefix = '';
	private $form_sufix = 'Form';
	private $form_file;
	private $form_path;
	private $form_izi_path;
	private $form_abstract_file;
	private $form_abstract_name;
	private $form_element;
	
	private $child_file;
	
	public function preDispatch() {
		Zend_Layout::getMvcInstance()->setLayout('install');
		
		$this->db_table = new Zend_Db_Table();
		
		$this->model_file = FILES_PATH . 'model.php';
		$this->model_path = ADMIN_PATH . '../../models/';
		$this->model_izi_path = $this->model_path . 'izi/';
		$this->model_abstract_file = FILES_PATH . 'model_abstract.php';
		$this->model_abstract_name = $this->nameModel('abstract');
		$this->model_getset_file = FILES_PATH . 'model_getset.php';
		$this->model_getfk_file = FILES_PATH . 'model_getfk.php';
		
		$this->dbtable_file = FILES_PATH . 'dbtable.php';
		$this->dbtable_path = ADMIN_PATH . '../../models/DbTable/';
		$this->dbtable_izi_path = $this->dbtable_path . 'izi/';
		$this->dbtable_abstract_file = FILES_PATH . 'dbtable_abstract.php';
		$this->dbtable_abstract_name = $this->nameDbTable('abstract');
		$this->dbtable_referencemap_file = FILES_PATH . 'dbtable_referencemap.php';
		$this->dbtable_find_file = FILES_PATH . 'dbtable_find.php';
		
		$this->form_file = FILES_PATH . 'form.php';
		$this->form_path = ADMIN_PATH . '../../forms/';
		$this->form_izi_path = $this->form_path . 'izi/';
		$this->form_abstract_file = FILES_PATH . 'form_abstract.php';
		$this->form_abstract_name = $this->nameForm('abstract');
		$this->form_element = FILES_PATH . 'form_element.php';
		
		$this->child_file = FILES_PATH . 'child.php';
		
		$this->makeDirs();
	}
	
	public function indexAction() {
		$this->view->tables = $this->getTables();
	}
	
	// Gera Model e DbTable
	public function generateAction() {
		try {
			$this->clearAction(false);
			$this->tables = $this->getRequest()->getParam('tables', false);
			$this->saveAbstracts();
			$this->saveMDFs();
			
		} catch (Exception $e) {
			print_r($e->getMessage());exit;
		}
		$this->_redirect('admin/install');
	}
	
	// Apagar arquivos
	public function clearAction($redirect = true) {
		try {
			$ponteiro = opendir($this->model_izi_path);
			while ($item = readdir($ponteiro)) {
				if (is_file($this->model_izi_path . $item))
					unlink($this->model_izi_path . $item);
			}
			
			$ponteiro = opendir($this->dbtable_izi_path);
			while ($item = readdir($ponteiro)) {
				if (is_file($this->dbtable_izi_path . $item))
					unlink($this->dbtable_izi_path . $item);
			}
			
			$ponteiro = opendir($this->form_izi_path);
			while ($item = readdir($ponteiro)) {
				if (is_file($this->form_izi_path . $item))
					unlink($this->form_izi_path . $item);
			}
		} catch (Exception $e) {
			print_r($e->getMessage());exit;
		}
		
		if ($redirect)
			$this->_redirect('admin/install');
	}
	
	// Salva Abstract Izis
	public function saveAbstracts() {
		$content = $this->content($this->model_abstract_file, array('##MODEL##'), array('Izi' . $this->model_abstract_name));
		$this->save($this->model_izi_path . 'Izi' . $this->model_abstract_name . '.php', $content);
		
		$file = $this->model_path . $this->model_abstract_name . '.php';
			if (!file_exists($file)) {
			$content = $this->content(
				$this->child_file,
				array(
					'##CHILD##',
					'##PARENT##',
					'##CONTENT##'
				),
				array(
					$this->model_abstract_name,
					'Izi' . $this->model_abstract_name,
					'// ...'
				)
			);
			$this->save($file, $content);
		}
		
		$content = $this->content($this->dbtable_abstract_file, array('##DBTABLE##'), array('Izi' . $this->dbtable_abstract_name));
		$this->save($this->dbtable_izi_path . 'Izi' . $this->dbtable_abstract_name . '.php', $content);
		
		$file = $this->dbtable_path . $this->dbtable_abstract_name . '.php';
		if (!file_exists($file)) {
			$content = $this->content(
				$this->child_file,
				array(
					'##CHILD##',
					'##PARENT##',
					'##CONTENT##'
				),
				array(
					$this->dbtable_abstract_name,
					'Izi' . $this->dbtable_abstract_name,
					'// ...'
				)
			);
			$this->save($file, $content);
		}
		
		$content = $this->content($this->form_abstract_file, array('##FORM##', '##DBTABLE##', '##MODEL##'), array('Izi' . $this->form_abstract_name, $this->dbtable_abstract_name, $this->model_abstract_name));
		$this->save($this->form_izi_path . 'Izi' . $this->form_abstract_name . '.php', $content);
		
		$file = $this->form_path . $this->form_abstract_name . '.php';
		if (!file_exists($file)) {
			$content = $this->content(
				$this->child_file,
				array(
					'##CHILD##',
					'##PARENT##',
					'##CONTENT##'
				),
				array(
					$this->form_abstract_name,
					'Izi' . $this->form_abstract_name,
					'public function general() {}'
				)
			);
			$this->save($file, $content);
		}
	}
	
	// Sava Models, DbTables e Forms
	public function saveMDFs() {
		foreach ($this->tables as $item => $value) {
			$this->saveIziModel($item);
			
			// Gera DbTable izi
			$this->saveIziDbTable($item);
			
			// Gera Form izi
			$this->saveIziForm($item);
			
		}
	}
	
	// Salva Model izi
	public function saveIziModel($item) {
		$this->save(
			$this->model_izi_path . $this->nameIziModel($item) . '.php',
			$this->contentIziModel($item)
		);
		
		$this->saveModel($item);
	}
	
	// Salva DbTable izi
	public function saveIziDbTable($item) {
		$this->save(
			$this->dbtable_izi_path . $this->nameIziDbTable($item) . '.php',
			$this->contentIziDbTable($item)
		);
		
		$this->saveDbTable($item);
	}
	
	// Salva Form izi
	public function saveIziForm($item) {
		$this->save(
			$this->form_izi_path . $this->nameIziForm($item) . '.php',
			$this->contentIziForm($item)
		);
		
		$this->saveForm($item);
	}
	
	// Salva Model
	public function saveModel($item) {
		$file = $this->model_path . $this->nameModel($item) . '.php';
		if (!file_exists($file)) {
			$content = $this->content(
				$this->child_file,
				array(
					'##CHILD##',
					'##PARENT##',
					'##CONTENT##'
				),
				array(
					$this->nameModel($item),
					$this->nameIziModel($item),
					'// ...'
				)
			);
			return $this->save($file, $content);
		}
	}
	
	// Salva DbTable
	public function saveDbTable($item) {
		$file = $this->dbtable_path . $this->nameDbTable($item) . '.php';
		if (!file_exists($file)) {
			$content = $this->content(
				$this->child_file,
				array(
					'##CHILD##',
					'##PARENT##',
					'##CONTENT##'
				),
				array(
					$this->nameDbTable($item),
					$this->nameIziDbTable($item),
					'// ...'
				)
			);
			return $this->save($file, $content);
		}
	}
	
	// Salva Form
	public function saveForm($item) {
		$file = $this->form_path . $this->nameForm($item) . '.php';
		if (!file_exists($file)) {
			$content = $this->content(
				$this->child_file,
				array(
					'##CHILD##',
					'##PARENT##',
					'##CONTENT##'
				),
				array(
					$this->nameForm($item),
					$this->nameIziForm($item),
					'public function config() {}'
				)
			);
			return $this->save($file, $content);
		}
	}
	
	// Nome do Model izi
	public function nameIziModel($name = '') {
		return $this->name('izi_' . $name, $this->model_prefix, $this->model_sufix);
	}
	
	// Nome do DbTable izi
	public function nameIziDbTable($name = '') {
		return $this->name('izi_' . $name, $this->dbtable_prefix, $this->dbtable_sufix);
	}
	
	// Nome do Form izi
	public function nameIziForm($name = '') {
		return $this->name('izi_' . $name, $this->form_prefix, $this->form_sufix);
	}
	
	// Nome do Model
	public function nameModel($name = '') {
		return $this->name($name, $this->model_prefix, $this->model_sufix);
	}
	
	// Nome do DbTable
	public function nameDbTable($name = '') {
		return $this->name($name, $this->dbtable_prefix, $this->dbtable_sufix);
	}

	// Nome do Form
	public function nameForm($name = '') {
		return $this->name($name, $this->form_prefix, $this->form_sufix);
	}
	
	// Conteudo do Model
	public function contentIziModel($name) {
		return $this->content($this->model_file, array(
			'##MODEL##',
			'##EXTENDS##',
			'##DATA##',
			'##DBTABLE##',
			'##GETSET##'
		), array(
			$this->nameIziModel($name),
			$this->model_abstract_name,
			$this->contentData($name),
			$this->nameDbTable($name),
			$this->contentModelGetSet($name),
		));
	}
	
	// Conteudo do DbTable
	public function contentIziDbTable($name) {
		return $this->content($this->dbtable_file, array(
			'##DBTABLE##',
			'##EXTENDS##',
			'##NAME##',
			'##MODEL##',
			'##DEPENDENTS##',
			'##REFERENCEMAP##',
			'##METADATA##',
			'##FIND##'
		), array(
			$this->nameIziDbTable($name),
			$this->dbtable_abstract_name,
			$name,
			$this->nameModel($name),
			$this->contentDependents($name),
			$this->contentReferencemap($name),
			$this->getMetaData($name),
			$this->contentDbTableFind($name)
		));
	}
	
	// Conteudo do Form
	public function contentIziForm($name) {
		return $this->content($this->form_file, array(
			'##FORM##',
			'##EXTENDS##',
			'##NAME##',
			'##ELEMENTS##'
		), array(
			$this->nameIziForm($name),
			$this->form_abstract_name,
			$name,
			$this->contentFormElements($name)
		));
	}
	
	// Conteudo do Model data
	public function contentData($name) {
		$content = array();
		$columns = $this->tables[$name];
		foreach ($columns as $item) {
			$content[] = "'" . $item . "' => ''"; 
		}
		return implode(', ', $content);
	}
	
	// Conteudo do Model getters e setters
	public function contentModelGetSet($name) {
		$content = '';
		$columns = $this->tables[$name];
		foreach ($columns as $item) {
			$content .= $this->content($this->model_getset_file, array(
				'##FUNCTION##',
				'##TYPE##',
				'##NAME##'
			), array(
				$this->name($item),
				$this->getModelColumnType($name, $item),
				$item
			));

			if (preg_match('/(.+)_id$/', $item, $matches)) {
				if ($matches[1] != $name) {
					$content .= $this->content($this->model_getfk_file, array(
						'##FUNCTION##',
						'##FIND##',
						'##DBTABLE##',
						'##CURRENT##'
					), array(
						$this->name($matches[1]),
						'findParentRow',
						$this->nameDbTable($matches[1]),
						''
				));
				}
			}
		}
		foreach ($this->tables as $item => $columns) {
			foreach ($columns as $subitem) {
				if ($name . '_id' == $subitem) {
					$content .= $this->content($this->model_getfk_file, array(
						'##FUNCTION##',
						'##FIND##',
						'##DBTABLE##',
						'##CURRENT##'
					), array(
						$this->name($item),
						'findDependentRowset',
						$this->nameDbTable($item),
						''
					));
				}
			}
		}
		
		return $content;
	}
	
	// Conteudo do DbTable $_referenceMap
	public function contentReferencemap($name, $toString = true) {
		$content = array();
		$columns = $this->tables[$name];
		foreach ($columns as $item) {
			if (preg_match('/(.+)_id$/', $item, $matches)) {
				$nameDbTable = $this->nameDbTable($matches[1]);
				$content[] = $this->content($this->dbtable_referencemap_file, array(
					'##NAME##',
					'##DBTABLE##',
					'##COLUMN##'
				), array(
					$matches[1],
					$nameDbTable,
					$item
				));
			}
		}
		
		if ($toString)
			if (count($content))
				return implode(',', $content);
			else
				return false;
		else
			return $content;
	}
	
	// Conteudo do DbTable $_dependentTables
	public function contentDependents($name) {
		$content = array();
		foreach ($this->tables as $item => $columns) {
			foreach ($columns as $subitem) {
				$nameDbTable = $this->nameDbTable($item);
				if ($name . '_id' == $subitem) {
					$content[] = $nameDbTable;
				}
			}
		}
		
		if (count($content))
			return "'" . implode("', '", $content) . "'";
		else
			return false;
	}
	
	// Conteudo do DbTable find*
	public function contentDbTableFind($name) {
		$content = '';
		$columns = $this->tables[$name];
		foreach ($columns as $item) {
			$content .= $this->content($this->dbtable_find_file, array(
				'##FUNCTION##',
				'##NAME##'
			), array(
				$this->name($item),
				$item
			));
		}
		return $content;
	}
	
	// Conteudo do Form elements
	public function contentFormElements($name) {
		$content = '';
		$columns = $this->tables[$name];
		foreach ($columns as $item) {
			$content .= $this->content($this->form_element, array(
				'##FIELD##',
				'##ATTRIBS##',
				'##TYPE##',
				'##VALUE##',
				'##VALIDATOR##',
				'##REQUIRED##',
				'##MULTIOPTIONS##'
			), $this->getFormElement($name, $item));
		}
		return $content;
	}
	
	// Retorna Form element
	public function getFormElement($table, $column) {
		$element = array(
			'field' => $column,
			'attribs' => '',
			'type' => 'text',
			'value' => '',
			'validator' => false,
			'required' => 'true',
			'multioptions' => ''
		);
		$column_type = $this->getColumnType($table, $column);
		
		if ($this->isColumnPK($table, $column)) {
			$element['type'] = 'hidden';
			$element['required'] = 'false';
		} else {
			$length = $this->getColumnLength($table, $column);
			if ($length)
				$element['attribs'] = ", 'maxlength' => '" . $length . "'";
			
			if ($column_type == 'date')
				$element['validator'] = "array('Date', false, array('format' => 'yyyy-MM-dd'))";
			if ($column_type == 'datetime')
				$element['validator'] = "array('Date', false, array('format' => 'yyyy-MM-dd HH:mm:ss'))";
			elseif (preg_match('/int$/', $column_type, $matches))
				$element['validator'] = "'int'";
			elseif ($length)
				$element['validator'] .= "array('StringLength', false, array(0, " . $length . "))";
				
			if ($this->isColumnNull($table, $column))
				$element['required'] = 'false';

			if (preg_match('/text$/', $column_type, $matches) || preg_match('/blob$/', $column_type, $matches)) {
				$element['type'] = 'textarea';
				$element['attribs'] .= ", 'cols' => '35', 'rows' => '10'";
			} elseif (preg_match('/(.+)_id$/', $column, $matches)) {
				$element['type'] = 'select';
				$element['multioptions'] = ", 'multiOptions' => \$this->getDbOptions('" . $matches[1] . "', 'Selecione')";
			} elseif (preg_match('/^enum\(\'(.+)\'\)/', $column_type, $matches)) {
				$explode = explode('\',\'', $matches[1]);
				$multiOptions = array();
				foreach ($explode as $item) {
					$multiOptions[] = '\'' . $item . '\'' . ' => \'' . $item . '\'';
				}
				$element['type'] = 'select';
				$element['multioptions'] = ", 'multiOptions' => array('' => 'Selecione', " . implode(', ', $multiOptions) . ")";
			}
			
			$element['value'] = $this->getValueDefault($table, $column);
				
			$element['attribs'] .= ", 'class' => '" . $element['type'] . " " . $element['type'] . "-" . $element['field'] . ($element['required'] == 'true' ? ' required' : '')  .  "'";
		}
		
		return $element;
	}
	
	// Gera nome em camel
	public function name($name = '', $prefix = '', $sufix = '') {
		return $prefix . str_replace(' ' , '', ucwords(str_replace('_', ' ', $name))) . $sufix;
	}
	
	// Copia conteudo do arquivo e troca variaveis
	public function content($file = '', $search = array(), $replace = array()) {
		$search[] = '##DATAHORA##';
		$replace[] = date('Y-m-d H:i:s');
		$content = file_get_contents($file);

		return str_replace($search, $replace, $content);
	}
	
	// Salva arquivo
	public function save($file, $content) {
		$new = fopen($file, 'w');
		fwrite($new, $content);
		fclose($new);
	}
	
	// Gera metadata
	public function getMetaData($table) {
		$columns = $this->db_table->getAdapter()->describeTable($table);
		return str_replace(
			'=>', ' => ', str_replace(
				',', ', ', str_replace(
					',)', ')', str_replace(
						' ', '', str_replace(
							"\n", '', var_export($columns, true)
						)
					)
				)
			)
		);
	}
	
	// Verifica se primary key
	public function isColumnPK($table, $column) {
		$columns = $this->db_table->getAdapter()->describeTable($table);
		if ($columns[$column]) {
			return $columns[$column]['PRIMARY'];
		}
	}
	
	// Verifica se nulo
	public function isColumnNull($table, $column) {
		$columns = $this->db_table->getAdapter()->describeTable($table);
		if ($columns[$column]) {
			return $columns[$column]['NULLABLE'];
		}
	}
	
	// Retorna primary key
	public function getColumnPk($table) {
		$columns = $this->db_table->getAdapter()->describeTable($table);
		foreach ($columns as $item) {
			if ($item['PRIMARY']) {
				return $item['COLUMN_NAME'];
			}
		}
	}
	
	// Retorna column type
	public function getColumnType($table, $column) {
		$columns = $this->db_table->getAdapter()->describeTable($table);
		if ($columns[$column]) {
			return $columns[$column]['DATA_TYPE'];
		}
	}
	
	// Retorna value default
	public function getValueDefault($table, $column) {
		$columns = $this->db_table->getAdapter()->describeTable($table);
		if ($columns[$column] && $columns[$column]['DEFAULT'] != null) {
			return $columns[$column]['DEFAULT'];
		}
	}
	
	// Retorna column length
	public function getColumnLength($table, $column) {
		$columns = $this->db_table->getAdapter()->describeTable($table);
		if ($columns[$column]) {
			return $columns[$column]['LENGTH'];
		}
	}
	
	// Retorna Model column type
	public function getModelColumnType($table, $column) {
		if (preg_match('/int$/', $this->getColumnType($table, $column), $matches)) {
			return '(int) ';
		} else {
			return '(string) ';
		}
	}
	
	// Verifica e cria pastas
	public function makeDirs() {
		if (!is_dir($this->model_path))
			mkdir($this->model_path, 0777);
		
		if (!is_dir($this->dbtable_path))
			mkdir($this->dbtable_path, 0777);
		
		if (!is_dir($this->form_path))
			mkdir($this->form_path, 0777);
		
		if (!is_dir($this->model_izi_path))
			mkdir($this->model_izi_path, 0777);

		if (!is_dir($this->dbtable_izi_path))
			mkdir($this->dbtable_izi_path, 0777);

		if (!is_dir($this->form_izi_path))
			mkdir($this->form_izi_path, 0777);
	}
	
	// Busca e verifica tabelas
	public function getTables() {
		$expr = '/([a-z\_]+).php/i'; // Melhorar expressão;
		
		$models_array = array();
		$ponteiro = opendir($this->model_izi_path);
		while ($item = readdir($ponteiro)) {
			if (preg_match($expr, $item, $matches)) {
		    	$models_array[] = $matches[1];
			}
		}
		
		$dbtables_array = array();
		$ponteiro = opendir($this->dbtable_izi_path);
		while ($item = readdir($ponteiro)) {
			if (preg_match($expr, $item, $matches)) {
		    	$dbtables_array[] = $matches[1];
			}
		}
		
		$tables = $this->db_table->getAdapter()->listTables();
		$tables_array = array();
		foreach ($tables as $item) {
			$in_models_array = 0;
			if (in_array($this->nameIziModel($item), $models_array)) {
				$in_models_array = 1;
			}
			
			$in_dbtables_array = 0; 
			if (in_array($this->nameIziDbTable($item), $dbtables_array)) {
				$in_dbtables_array = 1;
			}
			
			$item_class = '';
			if ($in_models_array && $in_dbtables_array) {
				$item_class = 'created';
			} elseif (!$in_models_array && !$in_dbtables_array) {
				$item_class = '';
			} elseif (!$in_models_array) {
				$item_class = 'not-model';
			} elseif (!$in_dbtables_array) {
				$item_class = 'not-dbtable';
			}
			
			$tables_array[$item] = array(
				'name' => $item,
				'class' => $item_class,
				'columns' => $this->db_table->getAdapter()->describeTable($item)
			);
			
		}
		return $tables_array;
	}
}